import json
import requests
import urllib.parse as urlparse
import time
import hashlib
import hmac
import base64
import sys

from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin2.api_client.common import (SyncQueryer)
from enum import Enum

class State(Enum):
  CANCEL = "cancel"
  WAIT = "wait"
  DONE = "done"
  CONVERT = "convert"
  FINALIZING = "finalizing"
  FAILED = "failed"

class OrderBy(Enum):
  ASC = "asc"
  DESC = "desc"
  ASC_UPDATED_AT = "asc_updated_at"
  DESC_UPDATED_AT = "desc_updated_at"

class Side(Enum):
  SELL = "sell"
  BUY = "buy"

class OrdType(Enum):
  MARKET = "market"
  LIMIT = "limit"
  STOP_MARKET = "stop_market"
  STOP_LIMIT = "stop_limit"
  POST_ONLY = "post_only"
  IOC_LIMIT = "ioc_limit"

class MaicoinAuth(requests.auth.AuthBase):
  def __init__(self, key):
    self._key = AuthKey.from_file(key)

  def _gen_signature(self, api_path, param_map):
    str_to_sign = api_path + '&' + self.encode_object(param_map)
    # print('str_to_sign = ' + str_to_sign)
    sig = hmac.new(self._key.secret_key_bytes, str_to_sign.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
    return sig

  def __call__(self, req: requests.PreparedRequest):
    assert req.method in ["GET", "POST"], "only support method GET and POST!"

    parsed = urlparse.urlparse(req.url)

    if req.method == "GET":
      params = dict(urlparse.parse_qsl(parsed.query))
    elif req.method == "POST":
      params = json.loads(req.body)

    nonce = int(time.time() * 1000)
    params["nonce"] = nonce
    params["path"] = parsed.path
    payload = base64.b64encode(json.dumps(params).encode('utf-8'))
    signature = hmac.new(self._key.secret_key_bytes, payload, digestmod=hashlib.sha256).hexdigest()

    if req.method == "GET":
      req.prepare_url(req.url, params)
    elif req.method == "POST":
      req.prepare_body(None, None, params)

    req.headers.update({"X-MAX-ACCESSKEY": self._key.access_key})
    req.headers.update({"X-MAX-PAYLOAD": payload})
    req.headers.update({"X-MAX-SIGNATURE": signature})
    req.headers.update({"Content-Type": "application/json"})
    req.prepare_headers(req.headers)

    # print(req.headers)
    # print(req.body)
    # print(req.url)

    return req

class MaicoinPrivateClient():
  def __init__(self, key_path: str, ip=None):
    self._auth = MaicoinAuth(key_path)
    self._url = "https://max-api.maicoin.com"

    self._query = SyncQueryer(api_host=self._url, auth=self._auth, ip=ip)

  def get_assets(self):
    method = "GET"
    path = "/api/v2/members/accounts"

    response = self._query.query(method=method, path=path)
    return response.json()

  def get_orders(self, market: str, state: State = State.WAIT, order_by: OrderBy = OrderBy.ASC, limit: int = 100):
    assert market, "market must be not None and not empty!"

    method = "GET"
    params = {
      'market': market,
      'state': state.value,
      'order_by': order_by.value,
      'limit': limit,
    }
    path = "/api/v2/orders"

    response = self._query.query(method=method, path=path, params=params)
    return response.json()

  def cancel_order(self, id: str = None, client_oid: str = None):
    count = len(list(filter(lambda x : x != None, [id, client_oid])))
    assert count == 1, "id and client_oid have one and only one!"

    method = "POST"
    json = {}
    if id is not None:
      json['id'] = id
    elif client_oid is not None:
      json['client_oid'] = client_oid
    path = "/api/v2/order/delete"

    response = self._query.query(method=method, path=path, json=json)
    return response.json()

  def submit_order(
      self,
      market: str,
      side: Side,
      volume: str,
      price: str = None,
      client_oid: str = None,
      ord_type: OrdType = None,
    ):
    assert market, "market must be not None and not empty!"
    assert side, "side must be not None!"
    assert volume, "volume must be not None and not empty!"

    method = "POST"
    json = {
      'market': market,
      'side': side.value,
      'volume': volume,
      'price': price,
      'client_oid': client_oid,
      'ord_type': ord_type and ord_type.value,
    }
    path = "/api/v2/orders"

    response = self._query.query(method=method, path=path, json=json)
    return response.json()

  def cancel_all(self, symbol: str):
    orders = self.get_orders(symbol)
    for order in orders:
      self.cancel_order(order['id'], order['client_oid']);

    return orders


# api doc url
# https://max.maicoin.com/documents/api_list/v2

if __name__ == "__main__":
  key_path = sys.argv[1]

  maicoin = MaicoinPrivateClient(key_path)
  info = maicoin.get_assets()
  #info = maicoin.get_orders("ethusdt")
  #info = maicoin.cancel_all("ethusdt")
  #info = maicoin.submit_order("ethusdt", Side.SELL, "0.005", "1900.54", "ETH{}".format(int(time.time() * 1000)), OrdType.LIMIT)
  print(json.dumps(info, indent=2))
